#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/device.h>

MODULE_AUTHOR("Arkins");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtual PCI Driver");

#define MHDA_DRV_NAME "mhda"
#define MHDA_TARGET_DRV_NAME "ata_piix"
#define ASSERT_PTR(ptr, ret) if ((ptr) == NULL) return (ret);

static int MHDA_Probe(struct pci_dev *dev, const struct pci_device_id *ent);
static void MHDA_Remove(struct pci_dev *dev);

typedef int (*MHDA_NativeDriverProbe)(struct pci_dev *dev, const struct pci_device_id *ent);
typedef void (*MHDA_NativeDriverRemove)(struct pci_dev *dev);

// 系统自带的SD驱动probe和remove指针
static MHDA_NativeDriverProbe g_NativeDriverProbe = NULL;
static MHDA_NativeDriverRemove g_NativeDriverRemove = NULL;

// 系统自带的驱动适配器
static struct pci_driver g_ataDriverAdapter = {
	.name	= MHDA_DRV_NAME,
	.probe	= MHDA_Probe,
	.remove	= MHDA_Remove
};
static struct pci_driver *g_ataDriver = NULL;

static int MHDA_Probe(struct pci_dev *dev, const struct pci_device_id *ent)
{
	ASSERT_PTR(dev, 0)
	if (g_NativeDriverProbe == NULL) {
		return 0;
	}
	printk("MHDA: MHDA_Probe\n");
	return g_NativeDriverProbe(dev, ent);
}

static void MHDA_Remove(struct pci_dev *dev)
{
	if (g_NativeDriverRemove == NULL) {
		return;
	}
	g_NativeDriverRemove(dev);
}

static int MHDA_ReplaceProbe(struct device_driver *drv, void *data)
{
	struct pci_driver *pciDrv;
	if (strcmp(drv->name, MHDA_TARGET_DRV_NAME) != 0) {
		return 0;
	}
	pciDrv = to_pci_driver(drv);
	g_ataDriver = pciDrv;
	ASSERT_PTR(pciDrv, 0);

	g_NativeDriverProbe = pciDrv->probe;
	g_NativeDriverRemove = pciDrv->remove;
	pciDrv->probe = MHDA_Probe;
	pciDrv->remove = MHDA_Remove;
	printk("MHDA: MHDA_ReplaceProbe %p, %p\n", g_NativeDriverProbe, pciDrv->probe);
	return 0;
}

static int MHDA_RestoreProbe(struct device_driver *drv, void *data)
{
	struct pci_driver *pciDrv;

	if (strcmp(drv->name, MHDA_TARGET_DRV_NAME) != 0) {
		return 0;
	}
	if (g_NativeDriverProbe == NULL || g_NativeDriverRemove == NULL) {
		return 0;
	}

	pciDrv = to_pci_driver(drv);
	ASSERT_PTR(pciDrv, 0);
	pciDrv->probe = g_NativeDriverProbe;
	pciDrv->remove = g_NativeDriverRemove;
	return 0;
}

static int MHDA_PrintDevice(struct device *dev, void *data)
{
	ASSERT_PTR(dev, 0);
	// ASSERT_PTR(dev->kobj, 0);
	// ASSERT_PTR(dev->kobj.name, 0);
	if (dev->driver == &g_ataDriver->driver) {
		printk("%s %s\n", dev->kobj.name, dev->type->name);
	}
	return 0;
}

static void MHDA_RegisterDriver(void)
{
	int ret;
    ret = pci_register_driver(&g_ataDriverAdapter);
    if (ret) {
        printk("MHDA: scsi_register_driver error\n");
		return;
    }
	if (g_ataDriverAdapter.driver.bus == NULL) {
		return;
	}
	bus_for_each_drv(g_ataDriverAdapter.driver.bus, NULL, NULL, MHDA_ReplaceProbe);
	bus_for_each_dev(g_ataDriverAdapter.driver.bus, NULL, NULL, MHDA_PrintDevice);
}

static void MHDA_UnRegisterDriver(void)
{
	bus_for_each_drv(g_ataDriverAdapter.driver.bus, NULL, NULL, MHDA_RestoreProbe);
    pci_unregister_driver(&g_ataDriverAdapter);
}

static int __init MHDA_Init(void)
{
    printk("MHDA: MHDA_init\n");
	MHDA_RegisterDriver();
    return 0;
}

static void __exit MHDA_Exit(void)
{
    printk("MHDA: MHDA_exit\n");
	MHDA_UnRegisterDriver();
}

module_init(MHDA_Init)
module_exit(MHDA_Exit)
